/* JavaLog - package tracker 
 * 
 * Copyright (C) 2009 Allyson Lima, Diego Pedro, Victor Freire
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software 
 * Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 */

package br.edu.ufcg.ccc.javalog.model;

import java.util.ArrayList;
import java.util.List;

import br.edu.ufcg.ccc.javalog.exceptions.BadAddressException;

/**
 * Address class
 * 
 * @author Allyson Lima, Diego Pedro, Victor Freire
 * @version 08/11/09
 */
public class Address {
	/**
	 * Valid Regions that can be used.
	 */
	private static List<String> validRegions = new ArrayList<String>();
	
	/**
	 * Region address attribute
	 */
	private String region;

	/**
	 * State address attribute
	 */
	private String state;

	/**
	 * City address attribute
	 */
	private String city;

	/**
	 * District address attribute
	 */
	private String district;

	/**
	 * Zipcode address attribute
	 */
	private String zipcode;

	/**
	 * Street address attribute
	 */
	private String street;

	/**
	 * Number address attribute
	 */
	private String number;
	
	static 
	{
		validRegions.add("Norte");
		validRegions.add("Nordeste");
		validRegions.add("Centro-Oeste");
		validRegions.add("Sudeste");
		validRegions.add("Sul");		
	}

	/**
	 * Constructor for Address Class
	 * 
	 * @param region
	 *            Region to the address
	 * @param state
	 *            State to the address
	 * @param city
	 *            City to the address
	 * @param district
	 *            District to the address
	 * @param zipcode
	 *            Zipcode to the address
	 * @param street
	 *            Street to the address
	 * @param number
	 *            Number to the address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public Address(String region, String state, String city, String district,
			String zipcode, String street, String number)
			throws BadAddressException {
		setRegion(region);
		setState(state);
		setCity(city);
		setDistrict(district);
		setZipcode(zipcode);
		setStreet(street);
		setNumber(number);
	}

	/**
	 * Return the region address
	 * 
	 * @return the region address
	 */
	public String getRegion() {
		return region;
	}

	/**
	 * Modifies the region address
	 * 
	 * @param region
	 *            Region address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setRegion(String region) throws BadAddressException {
		Validator.validateString(region, new BadAddressException(
				"Região inválida."));

		this.region = StringUtil.fixWhiteSpace(region);
	}

	/**
	 * Return the state address
	 * 
	 * @return the state address
	 */
	public String getState() {
		return state;
	}

	/**
	 * Modifies the state address
	 * 
	 * @param state
	 *            State address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setState(String state) throws BadAddressException {
		Validator.validateString(state, new BadAddressException(
				"Estado inválido."));

		this.state = StringUtil.fixWhiteSpace(state);
	}

	/**
	 * Return the city address
	 * 
	 * @return the city address
	 */
	public String getCity() {
		return city;
	}

	/**
	 * Modifies the city address
	 * 
	 * @param city
	 *            City address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setCity(String city) throws BadAddressException {
		Validator.validateString(city, new BadAddressException(
				"Cidade inválida."));
		this.city = StringUtil.fixWhiteSpace(city);
	}

	/**
	 * Return the district address
	 * 
	 * @return the district address
	 */
	public String getDistrict() {
		return district;
	}

	/**
	 * Modifies the district address
	 * 
	 * @param district
	 *            District address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setDistrict(String district) throws BadAddressException {
		Validator.validateString(district, new BadAddressException(
				"Bairro inválido."));
		this.district = StringUtil.fixWhiteSpace(district);
	}

	/**
	 * Return the zipcode address
	 * 
	 * @return the zipcode address
	 */
	public String getZipcode() {
		return zipcode;
	}

	/**
	 * Modifies the zipcode address
	 * 
	 * @param zipcode
	 *            Zipcode address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setZipcode(String zipcode) throws BadAddressException {
		Validator.validateString(zipcode, new BadAddressException(
				"CEP inválido."));
		zipcode = StringUtil.fixWhiteSpace(zipcode);
		if (StringUtil.containsInvalidChar(zipcode, "0123456789-") != null)
			throw new BadAddressException("'"
					+ StringUtil.containsInvalidChar(zipcode, "0123456789-")
					+ "' CEP incorreto.");
		if (zipcode.length() != 9)
			throw new BadAddressException("O CEP precisa ter 9 caracteres.");
		this.zipcode = zipcode;
	}

	/**
	 * Return the street address
	 * 
	 * @return the street address
	 */
	public String getStreet() {
		return street;
	}

	/**
	 * Modifies the street address
	 * 
	 * @param street
	 *            Street address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setStreet(String street) throws BadAddressException {
		Validator.validateString(street, new BadAddressException(
				"Endereço inválido."));
		this.street = StringUtil.fixWhiteSpace(street);
	}

	/**
	 * Return the number address
	 * 
	 * @return the number address
	 */
	public String getNumber() {
		return number;
	}

	/**
	 * Modifies the number address
	 * 
	 * @param number
	 *            Number address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setNumber(String number) throws BadAddressException {
		Validator.validateString(number, new BadAddressException(
				"Número inválido."));
		Validator.validateAddressNumber(number, new BadAddressException(
				"Número não pode ser negativo."));
		this.number = number;
	}

	/**
	 * Modifies the full address
	 * 
	 * @param adr
	 *            Address
	 * @throws BadAddressException
	 *             Invalid arguments
	 */
	public void setAddress(Address adr) throws BadAddressException {
		setRegion(adr.getRegion());
		setState(adr.getState());
		setCity(adr.getCity());
		setDistrict(adr.getDistrict());
		setZipcode(adr.getZipcode());
		setStreet(adr.getStreet());
		setNumber(adr.getNumber());
	}

	/**
	 * Returns the regions that can be chosen.
	 */
	public static List<String> getValidRegions() {
		return validRegions;
	}

	/**
	 * Describes the address
	 * 
	 * @return description of the address
	 */
	@Override
	public String toString() {
		return getRegion() + ", " + getState() + ", "
				+ getCity() + ", " + getDistrict() + ", " + getZipcode() + "\n"
				+ getStreet() + ", " + getNumber();
	}

	/**
	 * Compares two addresses
	 * 
	 * @return true if both addresses are equal
	 */
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Address)) {
			return false;
		}
		Address address = (Address) obj;
		return getRegion().equals(address.getRegion())
				&& getState().equals(address.getState())
				&& getCity().equals(address.getCity())
				&& getDistrict().equals(address.getDistrict())
				&& getZipcode().equals(address.getZipcode())
				&& getStreet().equals(address.getStreet())
				&& getNumber().equals(address.getNumber());
	}

	/**
	 * HashCode method
	 * 
	 * @return HashCode
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((city == null) ? 0 : city.hashCode());
		result = prime * result
				+ ((district == null) ? 0 : district.hashCode());
		result = prime * result + ((number == null) ? 0 : number.hashCode());
		result = prime * result + ((region == null) ? 0 : region.hashCode());
		result = prime * result + ((state == null) ? 0 : state.hashCode());
		result = prime * result + ((street == null) ? 0 : street.hashCode());
		result = prime * result + ((zipcode == null) ? 0 : zipcode.hashCode());
		return result;
	}

}
